/* Sentinel - IRC Statistical and Operator Services
** slog.c - Logging subsystem
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: slog.c,v 1.7 2003/12/01 13:59:58 wcampbel Exp $
*/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <stdlib.h>
#include "slog.h"
#include "token.h"
#include "support.h"
#include "mem.h"

struct Token log_levels[] =
  {
    { "NONE", L_NONE },
    { "DEBUG", L_DEBUG },
    { "INFO", L_INFO },
    { "NOTICE", L_NOTICE },
    { "WARNING", L_WARNING },
    { "ERR", L_ERR },
    { "CRIT", L_CRIT },
    { "ALERT", L_ALERT },
    { "EMERG", L_EMERG },
    { NULL, TOKEN_ERROR }
  };

struct LogVal
{
  FILE *fp;
  int log_level;
  int log_ts;
  char filename[LOG_FILE_LEN];
  char backupfmt[LOG_FILE_LEN];
};

static struct LogVal *log_table[MAX_LOGS];
static int log_debug = 0;

/* Generic logging routine */
void slog(int logno, int level, char *fmt, ...)
{
  va_list ap;
  char buf[LOG_BUFSIZE], fmtime[LOG_LINE];
  time_t tmp;

  if ((logno >= MAX_LOGS) || (logno < 0) || log_table[logno] == NULL)
  {
    if (logno != DEFAULT_LOG)
      slog(DEFAULT_LOG, L_WARNING, "Attempting to write to closed logno %d",
           logno);
    return ;
  }

  /* Logging is done if the level of this log is greater than or
  ** equal to the level specified in the conf. (like syslog)
  */
  if (level < log_table[logno]->log_level)
    return ;

  va_start(ap, fmt);
  vsnprintf(buf, LOG_BUFSIZE, fmt, ap);

  if (log_table[logno]->log_ts)
  {
    snprintf(fmtime, LOG_LINE, "%ld", (long)time(NULL));
  }
  else
  {
    tmp = time(NULL);
    strftime(fmtime, LOG_LINE, LOG_TIMEFMT, localtime(&tmp));
  }

  if (log_table[logno]->fp == NULL)
  {
    log_table[logno]->fp = fopen(log_table[logno]->filename, "a");
    if (log_table[logno]->fp == NULL)
      return ;
  }

  fprintf(log_table[logno]->fp, "(%s) %s\n", fmtime, buf);

  if (log_debug)
  {
    fprintf(stderr, "(%s) %s\n", fmtime, buf);
  }

  va_end(ap);
}

void init_logsystem(void)
{
  int i;

  log_debug = 0;
  for (i = 0; i < MAX_LOGS; i++)
    log_table[i] = NULL;
}

int init_log(int logno, char *filename, int log_level, char *backupfmt)
{
  struct LogVal *lv;

  /* XXX - there should someday be an errno like function */
  if ((logno >= MAX_LOGS) || (logno < 0))
    return 0;

  if (log_table[logno] != NULL)
  {
    /* If there was an existing log in this position, close it for now */
    if (log_table[logno]->fp != NULL)
      fclose(log_table[logno]->fp);
    free(log_table[logno]);
    log_table[logno] = NULL;
  }

  lv = smalloc(sizeof(struct LogVal));
  strlcpy(lv->filename, filename, LOG_FILE_LEN);
  strlcpy(lv->backupfmt, backupfmt, LOG_FILE_LEN);
  lv->log_level = log_level;
  /* Wait until the log is called first to fopen the log. */
  log_table[logno] = lv;
  return 1;
}

int fini_log(int logno)
{
  if ((logno >= MAX_LOGS) || (logno < 0))
    return 0;

  if (log_table[logno] != NULL)
  {
    if (log_table[logno]->fp != NULL)
      fclose(log_table[logno]->fp);
    free(log_table[logno]);
    log_table[logno] = NULL;
    return 1;
  }

  return 0;
}

void set_logdebug(int ldebug)
{
  log_debug = ldebug;
}

void set_loglevel(int logno, int level)
{
  if ((logno >= MAX_LOGS) || (logno < 0))
    return ;

  if (log_table[logno] != NULL)
    log_table[logno]->log_level = level;
}

void set_logts(int logno, int tsmode)
{
  if ((logno >= MAX_LOGS) || (logno < 0))
    return ;

  if (log_table[logno] != NULL)
    log_table[logno]->log_ts = tsmode;
}

void ResetLogs(void)
{
  char tmp[LOG_FILE_LEN];
  time_t t = time(NULL);
  struct tm *ltm;
  int i;

  /* Fix the date stamp so it's the logs of THAT day, not the previous
  ** day with today's date stamp on it
  */
  t = t - 86400;
  /* It's MUCH easier to manipulate this than all the vars inside the
  ** struct
  */
  ltm = localtime(&t);

  for (i = 0; i < MAX_LOGS; i++)
  {
    if (log_table[i] != NULL)
    {
      strftime(tmp, LOG_FILE_LEN - 1, log_table[i]->backupfmt, ltm);
      tmp[LOG_FILE_LEN - 1] = '\0';
      if (log_table[i]->fp != NULL)
      {
        fclose(log_table[i]->fp);
        log_table[i]->fp = NULL;
      }
      rename(log_table[i]->filename, tmp);
      /* Reopen it */
      slog(i, L_CRIT, "Started fresh logfile.");
    }
  }
}

void close_logfiles(void)
{
  int i;

  for (i = 0; i < MAX_LOGS; i++)
  {
    if (log_table[i] != NULL)
    {
      if (log_table[i]->fp != NULL)
        fclose(log_table[i]->fp);
      free(log_table[i]);
      log_table[i] = NULL;
    }
  }
}

void FlushLogs(void)
{
  int i;

  for (i = 0; i < MAX_LOGS; i++)
  {
    if (log_table[i] != NULL)
    {
      if (log_table[i]->fp != NULL)
        fclose(log_table[i]->fp);
      log_table[i]->fp = fopen(log_table[i]->filename, "a");
    }
  }
}
